// #include <iostream>
// #include <cstdlib>
// #include <vector>
// #include <cassert>
// #include <unistd.h>

// #define PROCESS_NUM 5

// // 先描述
// class subEp // EndPoint
// {
// public:
//     subEp(const std::string &name, pid_t subId,int writeFd)
//     :name_(name),subId_(subId),writeFd_(writeFd)
//     {}
// public:
//     std::string name_;
//     pid_t subId_;
//     int writeFd_;
// };

// int main()
// {
//     // 再组织
//     std::vector<subEp> subs;
//     // 1. 建立子进程并建立和子进程通信的信道
//     for (int i = 0; i < PROCESS_NUM; i++)
//     {
//         int fds[2];
//         int n = pipe(fds);
//         assert(n ==0);
//         (void)n;

//         pid_t id = fork();
//         if (id == 0)
//         {
//             // 子进程, 进行读取任务
//             close(fds[1]);
//             exit(0);
//         }
//         close(fds[0]);
//     }
//     // 2. 走到这里的就是父进程
//     // 父进程进行写入

//     // 3. 回收子进程资源
//     return 0;
// }

#include <iostream>
#include <string>
#include <vector>
#include <cstdlib>
#include <cassert>
#include <ctime>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define PROCESS_NUM 4

/////////////////////////////////////////////子进程要完成的某种任务  --- 模拟实现

// 函数指针 类型
typedef void (*func_t)();

// 这里就模拟实现了三种任务
void downLoadTask()
{
    // 执行任务的同时获取一下子进程的pid -- 便于观察
    std::cout << getpid() << ": 下载任务\n"
              << std::endl;
}

void ioTask()
{
    std::cout << getpid() << ": IO任务\n"
              << std::endl;
}

void flushTask()
{
    std::cout << getpid() << ": 刷新任务\n"
              << std::endl;
}

// 往out中插入任务集
void loadTaskFunc(std::vector<func_t> *out)
{
    assert(out);
    out->push_back(downLoadTask);
    out->push_back(ioTask);
    out->push_back(flushTask);
}

///////////////////////////////////////下面的代码模拟实现多进程程序
class subEp
{
public:
    subEp(pid_t subId, int writeFd)
        : subId_(subId), writeFd_(writeFd)
    {
        char nameBuffer[1024];
        snprintf(nameBuffer, sizeof nameBuffer, "process-%d[pid(%d)-fd(%d)]", num++, subId_, writeFd_);
        name_ = nameBuffer;
    }

public:
    static int num;
    std::string name_;
    pid_t subId_;
    int writeFd_;
};

// 因为num为static类型 不属于任何一个对象(实例) 所以必须在这里进行初始化
int subEp::num = 0;

int recvTask(int readFd)
{
    int code = 0;
    // 判断是否读取到管道中的数据
    ssize_t s = read(readFd, &code, sizeof code);
    // 读取到 4个字节
    if (s == 4)
        return code;
    // 父进程退出
    else if (s <= 0)
        return -1;
    else
        return 0;
}

void createSubProcess(std::vector<subEp> *subs, std::vector<func_t> &funcMap)
{
    for (int i = 0; i < PROCESS_NUM; i++)
    {
        int fds[2];
        int n = pipe(fds);
        assert(n == 0);
        (void)n;
        pid_t id = fork();
        if (id == 0)
        {
            close(fds[1]);
            while (true)
            {
                // 获取管道中的任务编号，如果没有发送，子进程应该进行阻塞等待
                int commandCode = recvTask(fds[0]);
                if (commandCode >= 0 && commandCode < funcMap.size())
                {
                    // 执行任务
                    funcMap[commandCode];
                }
                else if (commandCode == -1)
                {
                    // 检测到父进程退出 跳出循环
                    break;
                }
            }
            // 子进程退出
            exit(0);
        }
        // 关闭读文件描述符 fds[0]
        close(fds[0]);
        subEp sub(id, fds[1]);
        // 将创建出的子进程插入subEp对象当中
        subs->push_back(sub);
    }
}

void sendTask(const subEp &process, int taskNum)
{
    std::cout << "send task num: " << taskNum << " send to -> " << process.name_ << std::endl;
    int n = write(process.writeFd_, &taskNum, sizeof(taskNum));
    assert(n == sizeof(int));
    (void)n;
}

void loadBlanceControl(const std::vector<subEp> &subs, const std::vector<func_t> &funcMap, int count)
{
    int processnum = subs.size();
    int tasknum = funcMap.size();

    while (count)
    {
        // 这里就是负载均衡的体现
        // 1. 选择一个子进程 --> std::vector<subEp> -> index - 随机数
        int subIdx = rand() % processnum;
        // 2. 选择一个任务 --> std::vector<func_t> -> index
        int taskIdx = rand() % tasknum;
        // 3. 将任务发送给选择的进程
        sendTask(subs[subIdx], taskIdx);
        // 每次发送完休息1秒
        sleep(1);
        count--;
    }
    // 当write写入退出 说明读到0了 不再需要给子进程发送任务
    for (int i = 0; i < processnum; i++)
    {
        close(subs[i].writeFd_); // waitpid();
    }
}

void waitProcess(std::vector<subEp> processes)
{
    int processnum = processes.size();
    for(int i =0;i<processnum;i++)
    {
        waitpid(processes[i].subId_,nullptr,0);
        std::cout<<"wait sub process success ...: " << processes[i].subId_ << std::endl;
    }
}

int main()
{
    // 1. 加载任务集
    std::vector<func_t> funcMap;
    loadTaskFunc(&funcMap);
    // 2. 创建子进程
    std::vector<subEp> subs;
    createSubProcess(&subs, funcMap);
    // 3. 父进程控制子进程，负载均衡的向子进程发生任务码
    int taskCnt = 15;
    loadBlanceControl(subs, funcMap, taskCnt);
    // 4. 回收子进程信息
    waitProcess(subs);
    return 0;
}